<?php

class StdLibBasicOperators implements CliLibrary
{
	private static $operators = array(
		ExpOperator::TYPE_PREFIX => array(
			'+' => array(PREC_PREFIX, 'op_unaryplus', '+p'),
			'-' => array(PREC_PREFIX, 'op_unaryminus', '-p'),
		),
		ExpOperator::TYPE_POSTFIX => array(
			'!' => array(PREC_POSTFIX, 'op_factorial'),
		),
		ExpOperator::TYPE_LEFT => array(
			'*' => array(PREC_TERM, 'op_multiplication'),
			'/' => array(PREC_TERM, 'op_division'),
			'div' => array(PREC_TERM, 'op_intdivision'),
			'%' => array(PREC_TERM, 'op_modulo'),
			'mod' => array(PREC_TERM, 'op_modulo'),
			'+' => array(PREC_SUM, 'op_addition'),
			'-' => array(PREC_SUM, 'op_substraction'),
		),
		ExpOperator::TYPE_RIGHT => array(
			'^' => array(PREC_POWER, 'op_power'),
		)
	);

	public static function addLibrary (CliMode $mode)
	{
		$mode->addOperators(__CLASS__, self::$operators);
	}

	public static function op_factorial (ArgContext $args)
	{
		$args->operandCount(1);
		$num = $args->toNumber(0);
		$int = (int) $num;

		if ($num != $int)
		{
			throw new CliArgumentException(
				"Factorial can not perfomed on nonintegers");
		}
		if ($int < 0)
		{
			throw new CliArgumentException(
				"Factorial is only defined for nonnegative integers");
		}

		for ($sum = 1; $int > 1; $sum *= $int--);

		return $sum;
	}

	public static function op_unaryplus (ArgContext $args)
	{
		$args->operandCount(1);
		return 1 * $args->toNumber(0);
	}

	public static function op_unaryminus (ArgContext $args)
	{
		$args->operandCount(1);
		return -1 * $args->toNumber(0);
	}

	public static function op_multiplication (ArgContext $args)
	{
		$args->operandCount(2);
		return $args->toNumber(0) * $args->toNumber(1);
	}

	public static function op_division (ArgContext $args)
	{
		$args->operandCount(2);
		return $args->toNumber(0) / $args->toNumber(1);
	}

	public static function op_intdivision (ArgContext $args)
	{
		$args->operandCount(2);
		return (int) ($args->toNumber(0) / $args->toNumber(1));
	}

	public static function op_modulo (ArgContext $args)
	{
		$args->operandCount(2);
		return $args->toNumber(0) % $args->toNumber(1);
	}

	public static function op_addition (ArgContext $args)
	{
		$args->operandCount(2);
		return $args->toNumber(0) + $args->toNumber(1);
	}

	public static function op_substraction (ArgContext $args)
	{
		$args->operandCount(2);
		return $args->toNumber(0) - $args->toNumber(1);
	}

	public static function op_power (ArgContext $args)
	{
		$args->operandCount(2);
		return pow($args->toNumber(0), $args->toNumber(1));
	}
}